home *** CD-ROM | disk | FTP | other *** search
/ HAM Radio 3.2 / Ham Radio Version 3.2 (Chestnut CD-ROMs)(1993).ISO / packet / n17jsrc / ftpserv.c < prev    next >
C/C++ Source or Header  |  1991-07-14  |  16KB  |  650 lines

  1. /* Internet FTP Server server machine - see RFC 959
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  */
  4. #include <stdio.h>
  5. #include <ctype.h>
  6. #include <time.h>
  7. #ifdef    __TURBOC__
  8. #include <io.h>
  9. #include <dir.h>
  10. #endif
  11. #include "global.h"
  12. #include "mbuf.h"
  13. #include "socket.h"
  14. #include "ftp.h"
  15. #include "ftpserv.h"
  16. #include "proc.h"
  17. #include "dirutil.h"
  18. #include "files.h"
  19. #include "commands.h"
  20.  
  21. static void ftpserv __ARGS((int s,void *unused,void *p));
  22. static int pport __ARGS((struct sockaddr_in *sock,char *arg));
  23. static void ftplogin __ARGS((struct ftpserv *ftp,char *pass));
  24. static int sendit __ARGS((struct ftpserv *ftp,char *command,char *file));
  25. static int recvit __ARGS((struct ftpserv *ftp,char *command,char *file));
  26.  
  27. /* Command table */
  28. static char *commands[] = {
  29.     "user",
  30.     "acct",
  31.     "pass",
  32.     "type",
  33.     "list",
  34.     "cwd",
  35.     "dele",
  36.     "name",
  37.     "quit",
  38.     "retr",
  39.     "stor",
  40.     "port",
  41.     "nlst",
  42.     "pwd",
  43.     "xpwd",            /* For compatibility with 4.2BSD */
  44.     "mkd ",
  45.     "xmkd",            /* For compatibility with 4.2BSD */
  46.     "xrmd",            /* For compatibility with 4.2BSD */
  47.     "rmd ",
  48.     "stru",
  49.     "mode",
  50.     NULLCHAR
  51. };
  52.  
  53. /* Response messages */
  54. static char banner[] = "220 %s FTP version %s ready on %s\n";
  55. static char badcmd[] = "500 Unknown command\n";
  56. static char binwarn[] = "100 Warning: type is ASCII and %s appears to be binary\n";
  57. static char unsupp[] = "500 Unsupported command or option\n";
  58. static char givepass[] = "331 Enter PASS command\n";
  59. static char logged[] = "230 Logged in\n";
  60. static char loggeda[] = "230 Logged in as anonymous, restrictions apply\n";
  61. static char typeok[] = "200 Type %s OK\n";
  62. static char only8[] = "501 Only logical bytesize 8 supported\n";
  63. static char deleok[] = "250 File deleted\n";
  64. static char mkdok[] = "200 MKD ok\n";
  65. static char delefail[] = "550 Delete failed: %s\n";
  66. static char pwdmsg[] = "257 \"%s\" is current directory\n";
  67. static char badtype[] = "501 Unknown type \"%s\"\n";
  68. static char badport[] = "501 Bad port syntax\n";
  69. static char unimp[] = "502 Command not yet implemented\n";
  70. static char bye[] = "221 Goodbye!\n";
  71. static char nodir[] = "553 Can't read directory \"%s\": %s\n";
  72. static char cantopen[] = "550 Can't read file \"%s\": %s\n";
  73. static char sending[] = "150 Opening data connection for %s %s\n";
  74. static char cantmake[] = "553 Can't create \"%s\": %s\n";
  75. static char writerr[] = "552 Write error: %s\n";
  76. static char portok[] = "200 Port command okay\n";
  77. static char rxok[] = "226 File received OK\n";
  78. static char txok[] = "226 File sent OK\n";
  79. static char noperm[] = "550 Permission denied\n";
  80. static char noconn[] = "425 Data connection reset\n";
  81. static char lowmem[] = "421 System overloaded, try again later\n";
  82. static char notlog[] = "530 Please log in with USER and PASS\n";
  83. static char userfirst[] = "503 Login with USER first.\n";
  84. static char okay[] = "200 Ok\n";
  85.  
  86. static int Sftp = -1;    /* Prototype socket for service */
  87.  
  88. /* Start up FTP service */
  89. int
  90. ftpstart(argc,argv,p)
  91. int argc;
  92. char *argv[];
  93. void *p;
  94. {
  95.     struct sockaddr_in lsocket;
  96.     int s;
  97.  
  98.     if(Sftp != -1){
  99.         /* Already running! */
  100.         return 0;
  101.     }
  102.     psignal(Curproc,0);    /* Don't keep the parser waiting */
  103.     chname(Curproc,"FTP listener");
  104.  
  105.     lsocket.sin_family = AF_INET;
  106.     lsocket.sin_addr.s_addr = INADDR_ANY;
  107.     if(argc < 2)
  108.         lsocket.sin_port = IPPORT_FTP;
  109.     else
  110.         lsocket.sin_port = atoi(argv[1]);
  111.  
  112.     Sftp = socket(AF_INET,SOCK_STREAM,0);
  113.     bind(Sftp,(char *)&lsocket,sizeof(lsocket));
  114.     listen(Sftp,1);
  115.     for(;;){
  116.         if((s = accept(Sftp,NULLCHAR,(int *)NULL)) == -1)
  117.             break;    /* Service is shutting down */
  118.  
  119.         if(availmem() < Memthresh){
  120.             usprintf(s,lowmem);
  121.             shutdown(s,1);
  122.         } else {
  123.             /* Spawn a server */
  124.             newproc("ftpserv",2048,ftpserv,s,NULL,NULL,0);
  125.         }
  126.     }
  127.     return 0;
  128. }
  129. static void
  130. ftpserv(s,unused,p)
  131. int s;    /* Socket with user connection */
  132. void *unused;
  133. void *p;
  134. {
  135.     struct ftpserv ftp;
  136.     char **cmdp,buf[512],*arg,*cp,*cp1,*file,*mode;
  137.     long t;
  138.     int cnt,i;
  139.     struct sockaddr_in socket;
  140.  
  141.     sockmode(s,SOCK_ASCII);
  142.     memset((char *)&ftp,0,sizeof(ftp));    /* Start with clear slate */
  143.     ftp.data = -1;
  144.  
  145.     sockowner(s,Curproc);        /* We own it now */
  146.     ftp.control = s;
  147.     /* Set default data port */
  148.     i = SOCKSIZE;
  149.     getpeername(s,(char *)&socket,&i);
  150.     socket.sin_port = IPPORT_FTPD;
  151.     ASSIGN(ftp.port,socket);
  152.  
  153.     log(s,"open FTP");
  154.     time(&t);
  155.     cp = ctime(&t);
  156.     if((cp1 = strchr(cp,'\n')) != NULLCHAR)
  157.         *cp1 = '\0';
  158.     usprintf(s,banner,Hostname,Version,cp);
  159. loop:    if((cnt = recvline(s,buf,sizeof(buf))) == -1){
  160.         /* He closed on us */
  161.         goto finish;
  162.     }
  163.     if(cnt == 0){
  164.         /* Can't be a legal FTP command */
  165.         usprintf(ftp.control,badcmd);
  166.         goto loop;
  167.     }    
  168.     rip(buf);
  169. #ifdef    UNIX
  170.     /* Translate first word to lower case */
  171.     for(cp = buf;*cp != ' ' && *cp != '\0';cp++)
  172.         *cp = tolower(*cp);
  173. #else
  174.     /* Translate entire buffer to lower case */
  175.     for(cp = buf;*cp != '\0';cp++)
  176.         *cp = tolower(*cp);
  177. #endif
  178.     /* Find command in table; if not present, return syntax error */
  179.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  180.         if(strncmp(*cmdp,buf,strlen(*cmdp)) == 0)
  181.             break;
  182.     if(*cmdp == NULLCHAR){
  183.         usprintf(ftp.control,badcmd);
  184.         goto loop;
  185.     }
  186.     /* Allow only USER, PASS and QUIT before logging in */
  187.     if(ftp.cd == NULLCHAR || ftp.path == NULLCHAR){
  188.         switch(cmdp-commands){
  189.         case USER_CMD:
  190.         case PASS_CMD:
  191.         case QUIT_CMD:
  192.             break;
  193.         default:
  194.             usprintf(ftp.control,notlog);
  195.             goto loop;
  196.         }
  197.     }
  198.     arg = &buf[strlen(*cmdp)];
  199.     while(*arg == ' ')
  200.         arg++;
  201.  
  202.     /* Execute specific command */
  203.     switch(cmdp-commands){
  204.     case USER_CMD:
  205.         free(ftp.username);
  206.         ftp.username = strdup(arg);
  207.         usprintf(ftp.control,givepass);
  208.         break;
  209.     case TYPE_CMD:
  210.         switch(arg[0]){
  211.         case 'A':
  212.         case 'a':    /* Ascii */
  213.             ftp.type = ASCII_TYPE;
  214.             usprintf(ftp.control,typeok,arg);
  215.             break;
  216.         case 'l':
  217.         case 'L':
  218.             while(*arg != ' ' && *arg != '\0')
  219.                 arg++;
  220.             if(*arg == '\0' || *++arg != '8'){
  221.                 usprintf(ftp.control,only8);
  222.                 break;
  223.             }
  224.             ftp.type = LOGICAL_TYPE;
  225.             ftp.logbsize = 8;
  226.             usprintf(ftp.control,typeok,arg);
  227.             break;
  228.         case 'B':
  229.         case 'b':    /* Binary */
  230.         case 'I':
  231.         case 'i':    /* Image */
  232.             ftp.type = IMAGE_TYPE;
  233.             usprintf(ftp.control,typeok,arg);
  234.             break;
  235.         default:    /* Invalid */
  236.             usprintf(ftp.control,badtype,arg);
  237.             break;
  238.         }
  239.         break;
  240.     case QUIT_CMD:
  241.         usprintf(ftp.control,bye);
  242.         goto finish;
  243.     case RETR_CMD:
  244.         file = pathname(ftp.cd,arg);
  245.         switch(ftp.type){
  246.         case IMAGE_TYPE:
  247.         case LOGICAL_TYPE:
  248.             mode = READ_BINARY;
  249.             break;
  250.         case ASCII_TYPE:
  251.             mode = READ_TEXT;
  252.             break;
  253.         }
  254.         if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file)){
  255.              usprintf(ftp.control,noperm);
  256.         } else if((ftp.fp = fopen(file,mode)) == NULLFILE){
  257.             usprintf(ftp.control,cantopen,file,sys_errlist[errno]);
  258.         } else {
  259.             log(ftp.control,"RETR %s",file);
  260.             if(ftp.type == ASCII_TYPE && isbinary(ftp.fp)){
  261.                 usprintf(ftp.control,binwarn,file);
  262.             }
  263.             sendit(&ftp,"RETR",file);
  264.         }
  265.         free(file);
  266.         break;
  267.     case STOR_CMD:
  268.         file = pathname(ftp.cd,arg);
  269.         switch(ftp.type){
  270.         case IMAGE_TYPE:
  271.         case LOGICAL_TYPE:
  272.             mode = WRITE_BINARY;
  273.             break;
  274.         case ASCII_TYPE:
  275.             mode = WRITE_TEXT;
  276.             break;
  277.         }
  278.         if(!permcheck(ftp.path,ftp.perms,STOR_CMD,file)){
  279.              usprintf(ftp.control,noperm);
  280.         } else if((ftp.fp = fopen(file,mode)) == NULLFILE){
  281.             usprintf(ftp.control,cantmake,file,sys_errlist[errno]);
  282.         } else {
  283.             log(ftp.control,"STOR %s",file);
  284.             recvit(&ftp,"STOR",file);
  285.         }
  286.         free(file);
  287.         break;
  288.     case PORT_CMD:
  289.         if(pport(&ftp.port,arg) == -1){
  290.             usprintf(ftp.control,badport);
  291.         } else {
  292.             usprintf(ftp.control,portok);
  293.         }
  294.         break;
  295. #ifndef CPM
  296.     case LIST_CMD:
  297.         file = pathname(ftp.cd,arg);
  298.         if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file)){
  299.              usprintf(ftp.control,noperm);
  300.         } else if((ftp.fp = dir(file,1)) == NULLFILE){
  301.             usprintf(ftp.control,nodir,file,sys_errlist[errno]);
  302.         } else {
  303.             sendit(&ftp,"LIST",file);
  304.         }
  305.         free(file);
  306.         break;
  307.     case NLST_CMD:
  308.         file = pathname(ftp.cd,arg);
  309.         if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file)){
  310.              usprintf(ftp.control,noperm);
  311.         } else if((ftp.fp = dir(file,0)) == NULLFILE){
  312.             usprintf(ftp.control,nodir,file,sys_errlist[errno]);
  313.         } else {
  314.             sendit(&ftp,"NLST",file);
  315.         }
  316.         free(file);
  317.         break;
  318.     case CWD_CMD:
  319.         file = pathname(ftp.cd,arg);
  320.         if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file)){
  321.              usprintf(ftp.control,noperm);
  322.             free(file);
  323. #ifdef    MSDOS
  324.         /* Don'tcha just LOVE %%$#@!! MS-DOS? */
  325.         } else if(strcmp(file,"/") == 0 || access(file,0) == 0){
  326. #else
  327.         } else if(access(file,0) == 0){    /* See if it exists */
  328. #endif
  329.             /* Succeeded, record in control block */
  330.             free(ftp.cd);
  331.             ftp.cd = file;
  332.             usprintf(ftp.control,pwdmsg,file);
  333.         } else {
  334.             /* Failed, don't change anything */
  335.             usprintf(ftp.control,nodir,file,sys_errlist[errno]);
  336.             free(file);
  337.         }
  338.         break;
  339.     case XPWD_CMD:
  340.     case PWD_CMD:
  341.         usprintf(ftp.control,pwdmsg,ftp.cd);
  342.         break;
  343. #else
  344.     case LIST_CMD:
  345.     case NLST_CMD:
  346.     case CWD_CMD:
  347.     case XPWD_CMD:
  348.     case PWD_CMD:
  349. #endif
  350.     case ACCT_CMD:        
  351.         usprintf(ftp.control,unimp);
  352.         break;
  353.     case DELE_CMD:
  354.         file = pathname(ftp.cd,arg);
  355.         if(!permcheck(ftp.path,ftp.perms,DELE_CMD,file)){
  356.              usprintf(ftp.control,noperm);
  357.         } else if(unlink(file) == 0){
  358.             log(ftp.control,"DELE %s",file);
  359.             usprintf(ftp.control,deleok);
  360.         } else {
  361.             usprintf(ftp.control,delefail,sys_errlist[errno]);
  362.         }
  363.         free(file);
  364.         break;
  365.     case PASS_CMD:
  366.         if(ftp.username == NULLCHAR)
  367.             usprintf(ftp.control,userfirst);
  368.         else
  369.             ftplogin(&ftp,arg);            
  370.         break;
  371. #ifndef    CPM
  372.     case XMKD_CMD:
  373.     case MKD_CMD:
  374.         file = pathname(ftp.cd,arg);
  375.         if(!permcheck(ftp.path,ftp.perms,MKD_CMD,file)){
  376.             usprintf(ftp.control,noperm);
  377. #ifdef    UNIX
  378.         } else if(mkdir(file,0777) == 0){
  379. #else
  380.         } else if(mkdir(file) == 0){
  381. #endif
  382.             log(ftp.control,"MKD %s",file);
  383.             usprintf(ftp.control,mkdok);
  384.         } else {
  385.             usprintf(ftp.control,cantmake,file,sys_errlist[errno]);
  386.         }
  387.         free(file);
  388.         break;
  389.     case XRMD_CMD:
  390.     case RMD_CMD:
  391.         file = pathname(ftp.cd,arg);
  392.         if(!permcheck(ftp.path,ftp.perms,RMD_CMD,file)){
  393.              usprintf(ftp.control,noperm);
  394.         } else if(rmdir(file) == 0){
  395.             log(ftp.control,"RMD %s",file);
  396.             usprintf(ftp.control,deleok);
  397.         } else {
  398.             usprintf(ftp.control,delefail,sys_errlist[errno]);
  399.         }
  400.         free(file);
  401.         break;
  402.     case STRU_CMD:
  403.         if(tolower(arg[0]) != 'f')
  404.             usprintf(ftp.control,unsupp);
  405.         else
  406.             usprintf(ftp.control,okay);
  407.         break;
  408.     case MODE_CMD:
  409.         if(tolower(arg[0]) != 's')
  410.             usprintf(ftp.control,unsupp);
  411.         else
  412.             usprintf(ftp.control,okay);
  413.         break;
  414.     }
  415. #endif
  416.     goto loop;
  417. finish:
  418.     log(ftp.control,"close FTP");
  419.     /* Clean up */
  420.     close_s(ftp.control);
  421.     if(ftp.data != -1)
  422.         close_s(ftp.data);
  423.     if(ftp.fp != NULLFILE)
  424.         fclose(ftp.fp);
  425.     free(ftp.username);
  426.     free(ftp.path);
  427.     free(ftp.cd);
  428. }
  429.  
  430. /* Shut down FTP server */
  431. int
  432. ftp0(argc,argv,p)
  433. int argc;
  434. char *argv[];
  435. void *p;
  436. {
  437.     close_s(Sftp);
  438.     Sftp = -1;
  439.     return 0;
  440. }
  441. static
  442. int
  443. pport(sock,arg)
  444. struct sockaddr_in *sock;
  445. char *arg;
  446. {
  447.     int32 n;
  448.     int i;
  449.  
  450.     n = 0;
  451.     for(i=0;i<4;i++){
  452.         n = atoi(arg) + (n << 8);
  453.         if((arg = strchr(arg,',')) == NULLCHAR)
  454.             return -1;
  455.         arg++;
  456.     }
  457.     sock->sin_addr.s_addr = n;
  458.     n = atoi(arg);
  459.     if((arg = strchr(arg,',')) == NULLCHAR)
  460.         return -1;
  461.     arg++;
  462.     n = atoi(arg) + (n << 8);
  463.     sock->sin_port = n;
  464.     return 0;
  465. }
  466.  
  467. /* Attempt to log in the user whose name is in ftp->username and password
  468.  * in pass
  469.  */
  470. static void
  471. ftplogin(ftp,pass)
  472. struct ftpserv *ftp;
  473. char *pass;
  474. {
  475.     char *path;
  476.     int anony = 0;
  477.  
  478.     path = mallocw(200);
  479.     if((ftp->perms = userlogin(ftp->username,pass,&path,200,&anony))
  480.        == -1){
  481.         usprintf(ftp->control,noperm);
  482.         free(path);
  483.         return;
  484.     }
  485.     /* Set up current directory and path prefix */
  486. #if    defined(AMIGAGONE)
  487.     ftp->cd = pathname("", path);
  488.     ftp->path = strdup(ftp->cd);
  489.     free(path);
  490. #else
  491.     ftp->cd = path;
  492.     ftp->path = strdup(path);
  493. #endif
  494.  
  495.     if(!anony){
  496.         usprintf(ftp->control,logged);
  497.         log(ftp->control,"%s logged in",ftp->username);
  498.     } else {
  499.         usprintf(ftp->control,loggeda);
  500.         log(ftp->control,"%s logged in, ID %s",ftp->username,pass);
  501.     }
  502. }
  503.  
  504. #ifdef    MSDOS
  505. /* Illegal characters in a DOS filename */
  506. static char badchars[] = "\"[]:|<>+=;,";
  507. #endif
  508.  
  509. /* Return 1 if the file operation is allowed, 0 otherwise */
  510. int
  511. permcheck(path,perms,op,file)
  512. char *path;
  513. int perms;
  514. int op;
  515. char *file;
  516. {
  517.     char *cp, *cp1;
  518.  
  519.     if(file == NULLCHAR || path == NULLCHAR)
  520.         return 0;    /* Probably hasn't logged in yet */
  521. #ifdef    MSDOS
  522.     /* Check for characters illegal in MS-DOS file names */
  523.     for(cp = badchars;*cp != '\0';cp++){
  524.         if(strchr(file,*cp) != NULLCHAR)
  525.             return 0;    
  526.     }
  527. #endif
  528. #ifndef MAC
  529.     /* The target file must be under the user's allowed search path */
  530.     /* We let them specify multiple paths using path;path... -russ */
  531.     for(cp = path;;cp =cp1+1){
  532.         if((cp1=strchr(cp,';')) == NULLCHAR) cp1=strchr(cp,'\0');
  533.         if((strncmp(file,cp,cp1 - cp)) == 0) break;
  534.         if(*cp1 == '\0') return 0;
  535.     }
  536. #endif
  537.  
  538.     switch(op){
  539.     case RETR_CMD:
  540.         /* User must have permission to read files */
  541.         if(perms & FTP_READ)
  542.             return 1;
  543.         return 0;
  544.     case DELE_CMD:
  545.     case RMD_CMD:
  546.         /* User must have permission to (over)write files */
  547.         if(perms & FTP_WRITE)
  548.             return 1;
  549.         return 0;
  550.     case STOR_CMD:
  551.     case MKD_CMD:
  552.         /* User must have permission to (over)write files, or permission
  553.          * to create them if the file doesn't already exist
  554.          */
  555.         if(perms & FTP_WRITE)
  556.             return 1;
  557.         if(access(file,2) == -1 && (perms & FTP_CREATE))
  558.             return 1;
  559.         return 0;
  560.     }
  561.     return 0;    /* "can't happen" -- keep lint happy */
  562. }
  563. static int
  564. sendit(ftp,command,file)
  565. struct ftpserv *ftp;
  566. char *command;
  567. char *file;
  568. {
  569.     long total;
  570.     struct sockaddr_in dport;
  571.  
  572.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  573.     dport.sin_family = AF_INET;
  574.     dport.sin_addr.s_addr = INADDR_ANY;
  575.     dport.sin_port = IPPORT_FTPD;
  576.     bind(ftp->data,(char *)&dport,SOCKSIZE);
  577.     usprintf(ftp->control,sending,command,file);
  578.     if(connect(ftp->data,(char *)&ftp->port,SOCKSIZE) == -1){
  579.         fclose(ftp->fp);
  580.         ftp->fp = NULLFILE;
  581.         close_s(ftp->data);
  582.         ftp->data = -1;
  583.         usprintf(ftp->control,noconn);
  584.         return -1;
  585.     }
  586.     /* Do the actual transfer */
  587.     total = sendfile(ftp->fp,ftp->data,ftp->type,0);
  588.  
  589.     if(total == -1){
  590.         /* An error occurred on the data connection */
  591.         usprintf(ftp->control,noconn);
  592.         shutdown(ftp->data,2);    /* Blow away data connection */
  593.     } else {
  594.         usprintf(ftp->control,txok);
  595.     }
  596.     fclose(ftp->fp);
  597.     ftp->fp = NULLFILE;
  598.     close_s(ftp->data);
  599.     ftp->data = -1;
  600.     if(total == -1)
  601.         return -1;
  602.     else
  603.         return 0;
  604. }
  605. static int
  606. recvit(ftp,command,file)
  607. struct ftpserv *ftp;
  608. char *command;
  609. char *file;
  610. {
  611.     struct sockaddr_in dport;
  612.     long total;
  613.  
  614.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  615.     dport.sin_family = AF_INET;
  616.     dport.sin_addr.s_addr = INADDR_ANY;
  617.     dport.sin_port = IPPORT_FTPD;
  618.     bind(ftp->data,(char *)&dport,SOCKSIZE);
  619.     usprintf(ftp->control,sending,command,file);
  620.     if(connect(ftp->data,(char *)&ftp->port,SOCKSIZE) == -1){
  621.         fclose(ftp->fp);
  622.         ftp->fp = NULLFILE;
  623.         close_s(ftp->data);
  624.         ftp->data = -1;
  625.         usprintf(ftp->control,noconn);
  626.         return -1;
  627.     }
  628.     total = recvfile(ftp->fp,ftp->data,ftp->type,0);
  629.  
  630. #ifdef    CPM
  631.     if(ftp->type == ASCII_TYPE)
  632.         putc(CTLZ,ftp->fp);
  633. #endif
  634.     if(total == -1) {
  635.         /* An error occurred while writing the file */
  636.         usprintf(ftp->control,writerr,sys_errlist[errno]);
  637.         shutdown(ftp->data,2);    /* Blow it away */
  638.     } else {
  639.         usprintf(ftp->control,rxok);
  640.         close_s(ftp->data);
  641.     }
  642.     ftp->data = -1;
  643.     fclose(ftp->fp);
  644.     ftp->fp = NULLFILE;
  645.     if(total == -1)
  646.         return -1;
  647.     else
  648.         return 0;
  649. }
  650.